package com.tengju.bff.interfaces.covercharge.controller.provider;

import com.idanchuang.component.config.apollo.util.ConfigUtil;
import com.idanchuang.component.redis.util.DRedisLocks;
import com.tengju.bff.interfaces.covercharge.constant.CoverChargeErrorCode;
import com.tengju.bff.interfaces.covercharge.constant.OperateStatusEnum;
import com.tengju.bff.interfaces.covercharge.dto.ProfitSettleDetailReqDTO;
import com.tengju.bff.interfaces.covercharge.dto.ProfitSettleQueryReqDTO;
import com.tengju.bff.interfaces.covercharge.dto.ProfitSettleReqDTO;
import com.tengju.bff.interfaces.covercharge.dto.ServiceDealResDTO;
import com.tengju.bff.interfaces.covercharge.enums.OperateBizTypeEnum;
import com.tengju.bff.interfaces.covercharge.intf.IProfitSettleProvider;
import com.tengju.bff.interfaces.covercharge.service.IProfitSettleService;
import com.tengju.bff.interfaces.covercharge.service.IServiceRabbitMqService;
import com.tengju.bff.interfaces.covercharge.util.validator.ErrorMsg;
import com.tengju.bff.interfaces.covercharge.util.validator.ValidaUtil;
import com.tengju.bff.interfaces.shared.ApiResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestBody;

import javax.validation.Valid;
import java.util.Objects;

/**
 * @author liang_shi
 * @date 2020/7/31 14:31
 * @description 收益结算服务
 */
@Slf4j
@Service("profitSettleProvider")
public class ProfitSettleProvider implements IProfitSettleProvider {

    @Autowired
    private IProfitSettleService profitSettleService;

    @Autowired
    private IServiceRabbitMqService rabbitMqService;

    /**
     * 汇总结算key
     */
    private final static String CLEAR_SETTLEMENT_KEY = "SERVICE-PROFIT-SETTLE:";

    /**
     * 根据明细结算key
     */
    private final static String CLEAR_SETTLEMENT_DETAIL_KEY = "SERVICE-PROFIT-SETTLE-DETAIL:";


    @Override
    public ApiResponse<ServiceDealResDTO> clearSettlement(@RequestBody @Valid ProfitSettleReqDTO reqVo) {
        //幂等Id
        String uniqueId = reqVo.getUniqueId();
        String type = OperateBizTypeEnum.PROFIT_SETTLE.getType();
        return DRedisLocks.runWithLock(CLEAR_SETTLEMENT_KEY + uniqueId, 500, () -> {
            ServiceDealResDTO dealResDTO = new ServiceDealResDTO();
            dealResDTO.setUniqueId(uniqueId);
            try {
                log.info("收益结算处理开始,uniqueId:{},reqVo:{}", uniqueId, reqVo);
                if (Objects.isNull(reqVo.getChangeBalance()) && Objects.isNull(reqVo.getChangeProfit())) {
                    log.info("收益结算处理,业务数据为空,不处理");
                    //数据对象为空-失败
                    dealResDTO.setCode(OperateStatusEnum.FAILED.getStatus());
                    dealResDTO.setMsg(CoverChargeErrorCode.BUSINESS_DATA_NULL.getMsg());
                    dealResDTO.setNeedUpdate(Boolean.FALSE);
                    return ApiResponse.newFailure(CoverChargeErrorCode.BUSINESS_DATA_NULL.getCode(),CoverChargeErrorCode.BUSINESS_DATA_NULL.getMsg());
                }
                //幂等check
                ServiceDealResDTO settleRes = profitSettleService.querySettleResultByUniqueId(type, uniqueId);
                boolean reqResult = checkSettleReqResult(uniqueId, reqVo, settleRes, dealResDTO);
                log.info("收益结算处理,查询结算结果：{}，检查结果：{}", settleRes, dealResDTO);
                if (reqResult) {
                    return ApiResponse.newSuccess(dealResDTO);
                }
                //记录入库
                if (dealResDTO.isNeedAdd()) {
                    profitSettleService.createOperateRecord(type, uniqueId);
                }
                boolean result = doAccountProfitSettle(uniqueId, reqVo, dealResDTO);
                log.info("收益结算处理结束,结果,dealResDTO:{}", dealResDTO);
                if (result) {
                    return ApiResponse.newSuccess(dealResDTO);
                }
                //数据业务处理失败
                dealResDTO.setCode(OperateStatusEnum.FAILED.getStatus());
                return ApiResponse.newFailure(CoverChargeErrorCode.BUSINESS_FAIL.getCode(), dealResDTO.getMsg());
            } catch (Exception e) {
                log.error("收益结算业务处理异常:", e);
                dealResDTO.setCode(OperateStatusEnum.FAILED.getStatus());
                dealResDTO.setMsg(CoverChargeErrorCode.PROFIT_BUSINESS_EXCEPTION.getMsg());
                return ApiResponse.newFailure(CoverChargeErrorCode.PROFIT_BUSINESS_EXCEPTION.getCode(),CoverChargeErrorCode.PROFIT_BUSINESS_EXCEPTION.getMsg());
            } finally {
                //数据库更新操作-如果是幂等重试不更新
                if (dealResDTO.isNeedUpdate()) {
                    profitSettleService.modifyOperateRecord(uniqueId, dealResDTO.getCode(), dealResDTO.getMsg());
                }
                //发送Mq通知
                //sendNotifyMessage(uniqueId, dealResDTO);
            }
        });
    }

    @Override
    public ApiResponse<ServiceDealResDTO> clearSettlementByDetail(@RequestBody ProfitSettleDetailReqDTO reqVo) {
        //幂等Id
        String uniqueId = reqVo.getUniqueId();
        String type = OperateBizTypeEnum.PROFIT_SETTLE.getType();
        Integer dealSize = ConfigUtil.getConfig().getInteger("deal.profit.settle.size", 1000);
        return DRedisLocks.runWithLock(CLEAR_SETTLEMENT_DETAIL_KEY + uniqueId, 500, () -> {
            ServiceDealResDTO dealResDTO = new ServiceDealResDTO();
            dealResDTO.setUniqueId(uniqueId);
            try {
                ErrorMsg errorMsg = new ErrorMsg();
                if (!ValidaUtil.isReqParamCheck(reqVo, errorMsg)) {
                    log.info("收益明细结算处理,参数校验失败,不处理");
                    //数据对象为空-失败
                    dealResDTO.setCode(OperateStatusEnum.FAILED.getStatus());
                    dealResDTO.setMsg(errorMsg.getMessage());
                    dealResDTO.setNeedUpdate(Boolean.FALSE);
                    return ApiResponse.newFailure(CoverChargeErrorCode.BUSINESS_ERROR.getCode(),CoverChargeErrorCode.BUSINESS_ERROR.getMsg());
                }
                log.info("收益明细结算处理开始,uniqueId:{},reqVo:{}", uniqueId, reqVo);
                if (Objects.isNull(reqVo.getChangeBalance()) && Objects.isNull(reqVo.getChangeProfit())) {
                    log.info("收益明细结算处理,业务数据为空,不处理");
                    //数据对象为空-失败
                    dealResDTO.setCode(OperateStatusEnum.FAILED.getStatus());
                    dealResDTO.setMsg(CoverChargeErrorCode.BUSINESS_DATA_NULL.getMsg());
                    dealResDTO.setNeedUpdate(Boolean.FALSE);
                    return ApiResponse.newFailure(CoverChargeErrorCode.BUSINESS_DATA_NULL.getCode(),CoverChargeErrorCode.BUSINESS_DATA_NULL.getMsg());
                }
                if (!Objects.isNull(reqVo.getChangeProfit()) && !CollectionUtils.isEmpty(reqVo.getChangeProfit().getSettleProfit())) {
                    if (reqVo.getChangeProfit().getSettleProfit().size() >= dealSize) {
                        //超过业务处理数据量
                        dealResDTO.setCode(OperateStatusEnum.FAILED.getStatus());
                        dealResDTO.setMsg(CoverChargeErrorCode.EXC_BUSINESS_DEAL_DATA.getMsg() + "[" + dealSize + "]");
                        dealResDTO.setNeedUpdate(Boolean.FALSE);
                        return ApiResponse.newFailure(CoverChargeErrorCode.EXC_BUSINESS_DEAL_DATA.getCode(),CoverChargeErrorCode.EXC_BUSINESS_DEAL_DATA.getMsg());
                    }
                }
                //幂等check
                ServiceDealResDTO settleRes = profitSettleService.querySettleResultByUniqueId(type, uniqueId);
                boolean reqResult = checkSettleDetailReqResult(uniqueId, reqVo, settleRes, dealResDTO);
                log.info("收益明细结算处理,查询结算结果：{}，检查结果：{}", settleRes, dealResDTO);
                if (reqResult) {
                    dealResDTO.setBizNo(OperateBizTypeEnum.PROFIT_SETTLE);
                    return ApiResponse.newSuccess(dealResDTO);
                }
                //记录入库
                if (dealResDTO.isNeedAdd()) {
                    profitSettleService.createOperateRecord(type, uniqueId);
                }
                boolean result = doAccountProfitSettleByDetail(uniqueId, reqVo, dealResDTO);
                log.info("收益明细结算处理结束,结果,dealResDTO:{}", dealResDTO);
                if (result) {
                    dealResDTO.setBizNo(OperateBizTypeEnum.PROFIT_SETTLE);
                    return ApiResponse.newSuccess(dealResDTO);
                }
                //数据业务处理失败
                dealResDTO.setCode(OperateStatusEnum.FAILED.getStatus());
                return ApiResponse.newFailure(CoverChargeErrorCode.BUSINESS_FAIL.getCode(), dealResDTO.getMsg());
            } catch (Exception e) {
                log.error("收益明细结算业务处理异常:", e);
                dealResDTO.setCode(OperateStatusEnum.FAILED.getStatus());
                dealResDTO.setMsg(CoverChargeErrorCode.PROFIT_BUSINESS_EXCEPTION.getMsg());
                return ApiResponse.newFailure(CoverChargeErrorCode.PROFIT_BUSINESS_EXCEPTION.getCode(),CoverChargeErrorCode.PROFIT_BUSINESS_EXCEPTION.getMsg());
            } finally {
                //数据库更新操作-如果是幂等重试不更新
                if (dealResDTO.isNeedUpdate()) {
                    profitSettleService.modifyOperateRecord(uniqueId, dealResDTO.getCode(), dealResDTO.getMsg());
                }
            }
        });
    }

    /**
     * 处理结果查询
     *
     * @param reqDTO 请求
     * @return 结果
     */
    @Override
    public ApiResponse<ServiceDealResDTO> queryClearSettlementResult(@RequestBody ProfitSettleQueryReqDTO reqDTO) {
        String type = OperateBizTypeEnum.PROFIT_SETTLE.getType();
        if (StringUtils.isBlank(reqDTO.getUniqueId())) {
            return ApiResponse.newFailure(CoverChargeErrorCode.PARAM_ERROR.getCode(), "uniqueId不能为空");
        }
        ServiceDealResDTO dealRes = profitSettleService.querySettleResultByUniqueId(type, reqDTO.getUniqueId());
        return ApiResponse.newSuccess(dealRes);
    }

    /**
     * 校验结算结果
     *
     * @param uniqueId   幂等Id
     * @param reqVo      请求
     * @param settleRes  结算结果
     * @param dealResDTO 返回结果
     * @return 是否成功
     */
    private boolean checkSettleReqResult(String uniqueId, ProfitSettleReqDTO reqVo,
                                         ServiceDealResDTO settleRes, ServiceDealResDTO dealResDTO) {
        if (!Objects.isNull(settleRes)) {
            //只要有记录说明无需重复添加操作记录
            dealResDTO.setNeedAdd(Boolean.FALSE);
            //处理中 或者成功 直接返回
            if (OperateStatusEnum.PROCESSING.getStatus().equals(settleRes.getCode()) ||
                    OperateStatusEnum.SUCCESS.getStatus().equals(settleRes.getCode())) {
                dealResDTO.setCode(settleRes.getCode());
                dealResDTO.setMsg(settleRes.getMsg());
                //已存在的记录-无需更新操作
                dealResDTO.setNeedUpdate(Boolean.FALSE);
                return Boolean.TRUE;
            } else {
                //失败重试
                return doAccountProfitSettle(uniqueId, reqVo, dealResDTO);
            }
        }
        return Boolean.FALSE;
    }

    /**
     * 校验结算结果
     *
     * @param uniqueId   幂等Id
     * @param reqVo      请求
     * @param settleRes  结算结果
     * @param dealResDTO 返回结果
     * @return 是否成功
     */
    private boolean checkSettleDetailReqResult(String uniqueId, ProfitSettleDetailReqDTO reqVo,
                                               ServiceDealResDTO settleRes, ServiceDealResDTO dealResDTO) {
        if (!Objects.isNull(settleRes)) {
            //只要有记录说明无需重复添加操作记录
            dealResDTO.setNeedAdd(Boolean.FALSE);
            //处理中 或者成功 直接返回
            if (OperateStatusEnum.PROCESSING.getStatus().equals(settleRes.getCode()) ||
                    OperateStatusEnum.SUCCESS.getStatus().equals(settleRes.getCode())) {
                dealResDTO.setCode(settleRes.getCode());
                dealResDTO.setMsg(settleRes.getMsg());
                //已存在的记录-无需更新操作
                dealResDTO.setNeedUpdate(Boolean.FALSE);
                log.info("重复入账，幂等处理，uniqueId:{}",uniqueId);
                return Boolean.TRUE;
            } else {
                //失败重试
                return doAccountProfitSettleByDetail(uniqueId, reqVo, dealResDTO);
            }
        }
        return Boolean.FALSE;
    }


    /**
     * 处理账户收益结算入口
     *
     * @param uniqueId   幂等Id
     * @param reqVo      请求
     * @param dealResDTO 结果
     * @return 是否处理完成
     */
    private boolean doAccountProfitSettle(String uniqueId, ProfitSettleReqDTO reqVo, ServiceDealResDTO dealResDTO) {
        //操作账户核心
        boolean accResult = profitSettleService.dealAccountCoreSettle(uniqueId, reqVo.getChangeBalance(), dealResDTO);
        if (accResult) {
            //操作账户核心成功-操作服务费账户
            boolean serviceResult = profitSettleService.dealServiceCoreSettle(uniqueId, reqVo.getChangeProfit(), dealResDTO);
            if (serviceResult) {
                dealResDTO.setCode(OperateStatusEnum.SUCCESS.getStatus());
                dealResDTO.setMsg(OperateStatusEnum.SUCCESS.getMsg());
                return Boolean.TRUE;
            }
        }
        return Boolean.FALSE;
    }


    /**
     * 处理账户收益结算入口
     *
     * @param uniqueId   幂等Id
     * @param reqVo      请求
     * @param dealResDTO 结果
     * @return 是否处理完成
     */
    private boolean doAccountProfitSettleByDetail(String uniqueId, ProfitSettleDetailReqDTO reqVo, ServiceDealResDTO dealResDTO) {
        //操作账户核心
        boolean accResult = profitSettleService.dealAccountCoreSettle(uniqueId, reqVo.getChangeBalance(), dealResDTO);
        if (accResult) {
            //操作账户核心成功-操作服务费账户
            boolean serviceResult = profitSettleService.dealServiceCoreSettleByDetail(uniqueId, reqVo.getChangeProfit(), dealResDTO);
            if (serviceResult) {
                dealResDTO.setCode(OperateStatusEnum.SUCCESS.getStatus());
                dealResDTO.setMsg(OperateStatusEnum.SUCCESS.getMsg());
                return Boolean.TRUE;
            }
        }
        return Boolean.FALSE;
    }


    /**
     * 会员-通知收益结算服务处理结果
     *
     * @param resDTO 请求信息
     */
//    private void sendNotifyMessage(String uniqueId, ServiceDealResDTO resDTO) {
//        String topic = MqMessageConstants.SERVICE_MEMBER_PROFIT_SETTLE_NOTIFY;
//        String routeKey = MqMessageConstants.SERVICE_MEMBER_PROFIT_SETTLE_CALLBACK;
//        RabbitMqMessage mqMessage = new RabbitMqMessage();
//        String messageId = Objects.isNull(resDTO.getBizId()) ? uniqueId : uniqueId + "-" + resDTO.getBizId();
//        mqMessage.setMessageId(messageId);
//        mqMessage.setTopic(topic);
//        mqMessage.setRouteKey(routeKey);
//        mqMessage.setNum(0);
//        mqMessage.setMessageBody(resDTO);
//        mqMessage.setCreateTime(new Date());
//        //1分钟后消费
//        rabbitMqService.sendMessage(topic, routeKey, mqMessage, null);
//    }

}
